Let unicsv write something.
authoroliskoli <oliskoli>
Thu, 28 Jun 2007 22:17:24 +0000 (22:17 +0000)
committeroliskoli <oliskoli>
Thu, 28 Jun 2007 22:17:24 +0000 (22:17 +0000)
reference/expert-uni.txt [new file with mode: 0644]
testo
unicsv.c

diff --git a/reference/expert-uni.txt b/reference/expert-uni.txt
new file mode 100644 (file)
index 0000000..84427e8
--- /dev/null
@@ -0,0 +1,87 @@
+No,Latitude,Longitude,Name,Altitude,Description,Notes,Symbol,Date,Time\r
+1,42.438878,-71.119277,"5066",44.6,"5066","5066","Crossing",2001/11/28,22:05:28\r
+2,42.439227,-71.119689,"5067",57.6,"5067","5067","Dot",2001/06/02,05:26:55\r
+3,42.438917,-71.116146,"5096",44.8,"5096","5096","Dot",2001/11/17,00:03:38\r
+4,42.443904,-71.122044,"5142",50.6,"5142","5142","Dot",2001/11/28,22:05:28\r
+5,42.447298,-71.121447,"5156",127.7,"5156","5156","Dot",2001/06/02,05:26:58\r
+6,42.454873,-71.125094,"5224",96.9,"5224","5224","Dot",2001/06/02,05:26:59\r
+7,42.459079,-71.124988,"5229",82.6,"5229","5229","Dot",2001/06/02,05:26:59\r
+8,42.456979,-71.124474,"5237",82.9,"5237","5237","Dot",2001/06/02,05:26:59\r
+9,42.454401,-71.120990,"5254",66.7,"5254","5254","Dot",2001/11/28,22:05:28\r
+10,42.451442,-71.121746,"5258",74.6,"5258","5258","Dot",2001/11/08,00:53:41\r
+11,42.454404,-71.120660,"5264",65.3,"5264","5264","Dot",2001/11/28,22:05:28\r
+12,42.457761,-71.121045,"526708",77.4,"526708","526708","Dot",2001/06/02,05:27:00\r
+13,42.457089,-71.120313,"526750",74.7,"526750","526750","Dot",2001/06/02,05:27:00\r
+14,42.456592,-71.119676,"527614",78.7,"527614","527614","Dot",2001/11/08,00:53:41\r
+15,42.456252,-71.119356,"527631",78.7,"527631","527631","Dot",2001/11/08,00:53:41\r
+16,42.458148,-71.119135,"5278",68.3,"5278","5278","Dot",2001/06/02,05:27:00\r
+17,42.459377,-71.117693,"5289",64.0,"5289","5289","Dot",2001/06/02,05:27:01\r
+18,42.464183,-71.119828,"5374FIRE",53.0,"5374FIRE","5374FIRE","Dot",2001/11/28,22:05:28\r
+19,42.465650,-71.119399,"5376",56.4,"5376","5376","Dot",2001/06/02,05:27:02\r
+20,42.439018,-71.114456,"6006",56.4,"600698","600698","Dot",2001/06/02,05:26:55\r
+21,42.438594,-71.114803,"6006BLUE",46.0,"6006BLUE","6006BLUE","Dot",2001/11/28,22:05:28\r
+22,42.436757,-71.113223,"6014MEADOW",37.6,"6014MEADOW","6014MEADOW","Dot",2001/11/28,22:05:28\r
+23,42.441754,-71.113220,"6029",56.4,"6029","6029","Dot",2001/06/02,05:26:55\r
+24,42.436243,-71.109075,"6053",50.3,"6053","6053","Dot",2001/06/02,05:27:05\r
+25,42.439250,-71.107500,"6066",25.6,"6066","6066","Dot",2001/06/02,05:26:57\r
+26,42.439764,-71.107582,"6067",34.4,"6067","6067","Dot",2001/06/02,05:26:57\r
+27,42.434766,-71.105874,"6071",30.5,"6071","6071","Dot",2001/06/02,05:26:57\r
+28,42.433304,-71.106599,"6073",15.2,"6073","6073","Dot",2001/06/02,05:26:56\r
+29,42.437338,-71.104772,"6084",37.8,"6084","6084","Dot",2001/06/02,05:26:57\r
+30,42.442196,-71.110975,"6130",64.0,"6130","6130","Dot",2001/06/02,05:26:55\r
+31,42.442981,-71.111441,"6131",64.0,"6131","6131","Dot",2001/06/02,05:26:58\r
+32,42.444773,-71.108882,"6153",62.8,"6153","6153","Dot",2001/06/02,05:27:05\r
+33,42.443592,-71.106301,"6171",55.5,"6171","6171","Dot",2001/06/02,05:27:05\r
+34,42.447804,-71.106624,"6176",62.5,"6176","6176","Dot",2001/06/02,05:27:04\r
+35,42.448448,-71.106158,"6177",62.2,"6177","6177","Dot",2001/06/02,05:27:04\r
+36,42.453415,-71.106783,"6272",69.8,"6272","6272","Dot",2001/06/02,05:26:55\r
+37,42.453434,-71.107253,"6272",73.2,"6272","6272","Dot",2001/06/02,05:26:56\r
+38,42.458298,-71.106771,"6278",70.1,"6278","6278","Dot",2001/06/02,05:27:04\r
+39,42.451430,-71.105413,"6280",57.6,"6280","6280","Dot",2001/11/17,00:03:38\r
+40,42.453845,-71.105206,"6283",66.7,"6283","6283","Dot",2001/11/17,00:03:38\r
+41,42.459986,-71.106170,"6289",72.9,"6289","6289","Dot",2001/11/17,00:03:38\r
+42,42.457616,-71.105116,"6297",72.8,"6297","6297","Dot",2001/06/02,05:27:04\r
+43,42.467110,-71.113574,"6328",53.6,"6328","6328","Dot",2001/06/02,05:27:02\r
+44,42.464202,-71.109863,"6354",43.9,"6354","6354","Dot",2001/06/02,05:27:03\r
+45,42.466459,-71.110067,"635722",48.8,"635722","635722","Dot",2001/06/02,05:27:02\r
+46,42.466557,-71.109410,"635783",49.1,"635783","635783","Dot",2001/06/02,05:27:02\r
+47,42.463495,-71.107117,"6373",62.5,"6373","6373","Dot",2001/06/02,05:27:03\r
+48,42.401051,-71.110241,"6634",4.0,"6634","6634","Dot",2001/06/02,05:26:56\r
+49,42.432621,-71.106532,"6979",13.4,"6979","6979","Dot",2001/06/02,05:26:56\r
+50,42.431033,-71.107883,"6997",34.0,"6997","6997","Dot",2001/11/17,00:03:38\r
+51,42.465687,-71.107360,"BEAR HILL",87.8,"BEAR HILL TOWER","Bear Hill Tower","Tall Tower",2001/06/02,05:27:03\r
+52,42.430950,-71.107628,"BELLEVUE",23.5,"BELLEVUE","Bellevue Parking Lot","Parking Area",2001/06/02,02:18:15\r
+53,42.438666,-71.114079,"6016",43.4,"Bike Loop Connector","Bike Loop Connector","Waypoint",2001/11/28,22:05:28\r
+54,42.456469,-71.124651,"5236BRIDGE",89.9,"Bridge","Bridge","Bridge",2001/06/02,05:26:59\r
+55,42.465759,-71.119815,"5376BRIDGE",55.5,"Bridge","Bridge","Bridge",2001/06/02,05:27:01\r
+56,42.442993,-71.105878,"6181CROSS",52.7,"Crossing","Crossing","Crossing",2001/06/02,05:27:05\r
+57,42.435472,-71.109664,"6042CROSS",45.1,"Crossing","Crossing","Crossing",2001/06/02,05:27:05\r
+58,42.458516,-71.103646,"DARKHOLLPO",,"Dark Hollow Pond","Dark Hollow Pond","Fishing Area",,\r
+59,42.443109,-71.112675,"6121DEAD",56.1,"Dead End","Dead End","Danger Area",2001/06/02,05:26:57\r
+60,42.449866,-71.119298,"5179DEAD",117.0,"Dead End","Dead End","Danger Area",2001/06/02,05:26:59\r
+61,42.459629,-71.116524,"5299DEAD",69.5,"Dead End","Dead End","Danger Area",2001/06/02,05:27:01\r
+62,42.465485,-71.119148,"5376DEAD",57.0,"Dead End","Dead End","Danger Area",2001/06/02,05:27:02\r
+63,42.462776,-71.109986,"6353DEAD",46.9,"Dead End","Dead End","Danger Area",2001/06/02,05:27:03\r
+64,42.446793,-71.108784,"6155DEAD",61.3,"Dead End","Dead End","Danger Area",2001/06/02,05:27:04\r
+65,42.451204,-71.126602,"GATE14",110.9,"Gate 14","Gate 14","Truck Stop",2001/06/02,05:26:59\r
+66,42.458499,-71.122078,"GATE16",77.7,"Gate 16","Gate 16","Truck Stop",2001/06/02,05:27:00\r
+67,42.459376,-71.119238,"GATE17",65.8,"Gate 17","Gate 17","Truck Stop",2001/06/02,05:27:01\r
+68,42.466353,-71.119240,"GATE19",57.3,"Gate 19","Gate 19","Truck Stop",2001/06/02,05:27:02\r
+69,42.468655,-71.107697,"GATE21",49.4,"Gate 21","Gate 21","Truck Stop",2001/06/02,05:27:03\r
+70,42.456718,-71.102973,"GATE24",81.1,"Gate 24","Gate 24","Truck Stop",2001/06/02,05:27:03\r
+71,42.430847,-71.107690,"GATE5",21.5,"Gate 5","Gate 5","Truck Stop",2001/11/28,22:05:28\r
+72,42.431240,-71.109236,"GATE6",26.6,"Gate 6","Gate 6","Waypoint",2001/11/08,00:53:41\r
+73,42.439502,-71.106556,"6077LOGS",32.0,"Log Crossing","Log Crossing","Amusement Park",2001/06/02,02:18:16\r
+74,42.449765,-71.122320,"5148NANEPA",119.8,"Nanepashemet Road Crossing","Nanepashemet Road Crossing","Waypoint",2001/11/08,00:53:41\r
+75,42.457388,-71.119845,"5267OBSTAC",73.8,"Obstacle","Obstacle","Amusement Park",2001/06/02,05:27:00\r
+76,42.434980,-71.109942,"PANTHRCAVE",45.3,"Panther Cave","Panther Cave","Tunnel",2001/11/08,00:53:41\r
+77,42.453256,-71.121211,"5252PURPLE",78.0,"Purple Rock Hill","Purple Rock Hill","Summit",2001/11/08,00:53:41\r
+78,42.457734,-71.117481,"5287WATER",68.0,"Reservoir","Reservoir","Swimming Area",2001/06/02,05:27:01\r
+79,42.459278,-71.124574,"5239ROAD",81.1,"Road","Road","Truck Stop",2001/06/02,05:27:00\r
+80,42.458782,-71.118991,"5278ROAD",67.4,"Road","Road","Truck Stop",2001/06/02,05:27:01\r
+81,42.439993,-71.120925,"5058ROAD",53.9,"ROAD CROSSING","Road Crossing","Dot",2001/06/02,02:18:14\r
+82,42.453415,-71.106782,"SHEEPFOLD",69.8,"Sheepfold Parking Lot","Sheepfold Parking Lot","Parking Area",2001/06/02,02:18:13\r
+83,42.455956,-71.107483,"SOAPBOX",64.0,"Soap Box Derby Track","Soap Box Derby Track","Cemetery",2001/06/02,05:27:04\r
+84,42.465913,-71.119328,"5376STREAM",64.5,"Stream Crossing","Stream Crossing","Bridge",2001/11/08,00:53:41\r
+85,42.445359,-71.122845,"5144SUMMIT",61.6,"Summit","Summit","Summit",2001/11/28,22:05:28\r
+86,42.441727,-71.121676,"5150TANK",67.4,"WATER TANK","Water Tank","Museum",2001/06/02,02:18:16\r
diff --git a/testo b/testo
index a56eae4dd8f920d0b89c91d2b082b51597d43fa5..ceb82e1619d50272ea417bb32424aecfadb9b4f7 100755 (executable)
--- a/testo
+++ b/testo
@@ -1033,6 +1033,8 @@ echo "lat,lon,descr,name,notes,unk,unk" > ${TMPDIR}/unicsv.txt
 cat ${REFERENCE}/mxf.mxf >> ${TMPDIR}/unicsv.txt
 ${PNAME} -i unicsv -f ${TMPDIR}/unicsv.txt -o gpx -F ${TMPDIR}/unicsv.gpx
 compare ${TMPDIR}/unicsv.gpx ${REFERENCE}/unicsv.gpx
+${PNAME} -i gpx -f ${REFERENCE}/expertgps.gpx -o unicsv -F ${TMPDIR}/expert-uni.txt
+compare ${TMPDIR}/expert-uni.txt ${REFERENCE}/expert-uni.txt
 
 #
 # Basic NMEA tests
index 42602a1142fbec3135a20ce4a307e2b0eaf3d6ea..2192bd2cef86aa45e902af4060b43cf91500e825 100644 (file)
--- a/unicsv.c
+++ b/unicsv.c
@@ -1,7 +1,8 @@
 /*
     Universal CSV - support for csv files, divining field order from the header.
 
-    Copyright (C) 2006 Robert Lipe,  robertlipe@usa.net
+    Copyright (C) 2006 Robert Lipe, robertlipe@usa.net,
+    copyright (C) 2007 Olaf Klein, o.b.klein@gpsbabel.org
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 */
 
 #include "defs.h"
+#include "cet.h"
+#include "cet_util.h"
 #include "csv_util.h"
+#include "garmin_tables.h"
 #include "jeeps/gpsmath.h"
 #include "strptime.h"
 #include <string.h>
+#include <time.h>
 
 #define MYNAME "unicsv"
 
+/* "UNICSV_FIELD_SEP" and "UNICSV_LINE_SEP" are only used by the writer */
+#define UNICSV_FIELD_SEP       ","
+#define UNICSV_LINE_SEP                "\r\n"
+#define UNICSV_QUOT_CHAR       '"'
+
+/* GPSBabel internal and calculated fields */
 typedef enum {
        fld_shortname = 0,
        fld_latitude,
@@ -35,7 +46,7 @@ typedef enum {
        fld_url,
        fld_altitude,
        fld_utm_zone,
-       fld_utm_char,
+       fld_utm_zone_char,
        fld_utm_northing,
        fld_utm_easting,
        fld_utm,
@@ -58,6 +69,11 @@ typedef enum {
        fld_cadence,
        fld_proximity,
        fld_depth,
+       fld_symbol,
+       fld_date,
+       fld_time,
+       fld_datetime,
+       fld_iso_time,
        fld_terminator
 } field_e;
 
@@ -71,35 +87,44 @@ typedef struct {
        char *name;
        field_e type;
        gbuint32 options;
-       
 } field_t;
 
 /*
- * ! Use always underscores in field names !
+ * ! Please use always underscores in field names !
  * we check a second time after replacing underscores with spaces
  */
 static field_t fields_def[] = {
        { "name",       fld_shortname, STR_ANY },
        { "desc",       fld_description, STR_ANY },
        { "notes",      fld_notes, STR_ANY },
-       { "comment",    fld_notes, STR_ANY },
+       { "omment",     fld_notes, STR_ANY },
        { "text",       fld_notes, STR_ANY },
        { "url",        fld_url, STR_ANY },
+       { "icon",       fld_symbol, STR_ANY },
+       { "symb",       fld_symbol, STR_ANY },
        { "lat",        fld_latitude, STR_ANY },
        { "lon",        fld_longitude, STR_ANY },
        { "x",          fld_longitude, STR_EQUAL },
        { "y",          fld_latitude, STR_EQUAL },
+       { "z",          fld_altitude, STR_EQUAL },
+       { "x_pos",      fld_longitude, STR_ANY },
+       { "y_pos",      fld_latitude, STR_ANY },
        { "alt",        fld_altitude, STR_ANY },
        { "ele",        fld_altitude, STR_ANY },
        { "utm_z",      fld_utm_zone, STR_ANY },
-       { "utm_c",      fld_utm_char, STR_ANY },
+       { "utm_c",      fld_utm_zone_char, STR_ANY },
+       { "utm_zc",     fld_utm_zone_char, STR_ANY },
        { "utm_n",      fld_utm_northing, STR_ANY },
        { "utm_e",      fld_utm_easting, STR_ANY },
        { "utm",        fld_utm, STR_EQUAL },
+       { "utm_coo",    fld_utm, STR_ANY },
+       { "utm_pos",    fld_utm, STR_ANY },
        { "bng_z",      fld_bng_zone, STR_ANY },
        { "bng_n",      fld_bng_northing, STR_ANY },
        { "bng_e",      fld_bng_easting, STR_ANY },
        { "bng",        fld_bng, STR_EQUAL },
+       { "bng_coo",    fld_bng, STR_ANY },
+       { "bng_pos",    fld_bng, STR_ANY },
        { "hdop",       fld_hdop, STR_ANY },
        { "pdop",       fld_pdop, STR_ANY },
        { "vdop",       fld_vdop, STR_ANY },
@@ -108,14 +133,20 @@ static field_t fields_def[] = {
        { "utc_d",      fld_utc_date, STR_ANY },
        { "utc_t",      fld_utc_time, STR_ANY },
        { "head",       fld_course, STR_ANY },
-       { "course",     fld_course, STR_ANY },
+       { "cour",       fld_course, STR_ANY },
        { "speed",      fld_speed, STR_ANY },
+       { "geschw",     fld_speed, STR_ANY },           /* speed in german */
        { "tempf",      fld_temperature_f, STR_EQUAL }, /* degrees fahrenheit */
        { "temp",       fld_temperature, STR_ANY },     /* degrees celsius by default */
        { "heart",      fld_heartrate, STR_ANY },
-       { "cadence",    fld_cadence, STR_ANY },
+       { "caden",      fld_cadence, STR_ANY },
        { "prox",       fld_proximity, STR_ANY },
        { "depth",      fld_depth, STR_ANY },
+       { "date",       fld_date, STR_ANY },
+       { "time",       fld_time, STR_ANY },
+       /* unhandled columns */
+       { "index",      fld_terminator, STR_ANY },
+       { "no",         fld_terminator, STR_EQUAL },
        { NULL,         fld_terminator, 0 }
 };
 
@@ -123,11 +154,22 @@ static field_e *unicsv_fields_tab;
 static int unicsv_fields_tab_ct;
 static double unicsv_altscale;
 static char *unicsv_fieldsep;
-static gbfile *file_in;
+static gbfile *fin, *fout;
 static gpsdata_type unicsv_data_type;
-static route_head *unicsv_track;
+static route_head *unicsv_track, *unicsv_route;
+static unsigned long long unicsv_outp_flags;
+static grid_type unicsv_grid_idx;
+static int unicsv_datum_idx;
+static char *opt_datum, *opt_grid;
+static int unicsv_waypt_ct;
+static char unicsv_detect;
 
-static arglist_t unicsv_args[] = { ARG_TERMINATOR };
+static arglist_t unicsv_args[] = { 
+       {"datum", &opt_datum, "GPS datum (def. WGS 84)",
+               "WGS 84", ARGTYPE_STRING, ARG_NOMINMAX}, 
+       {"grid",  &opt_grid,  "Write position using this grid.",
+               NULL, ARGTYPE_STRING, ARG_NOMINMAX},
+       ARG_TERMINATOR };
 
 
 /* helpers */
@@ -135,6 +177,73 @@ static arglist_t unicsv_args[] = { ARG_TERMINATOR };
 // #define UNICSV_IS(f) (0 == strcmp(s, f))
 #define UNICSV_CONTAINS(f) (0 != strstr(s, f))
 
+/* here we only need a simple yes(0) or no(1) */
+static int
+unicsv_strrcmp(const char *s1, const char *s2)
+{
+       int l1, l2;
+       
+       l1 = strlen(s1);
+       l2 = strlen(s2);
+       if ((l1 - l2) >= 0)
+               return strcmp(s1 + (l1 - l2), s2);
+       else
+               return 1;       /* false */
+}
+
+static int
+unicsv_parse_date(const char *str)
+{
+       int p1, p2, p3, ct;
+       char sep[2];
+       struct tm tm;
+       
+       memset(&tm, 0, sizeof(tm));
+       ct = sscanf(str, "%d%1[-.//]%d%1[-.//]%d", &p1, sep, &p2, sep, &p3);
+       is_fatal(ct != 5, MYNAME ": Could not parse date string (%s).", str);
+       
+       if ((p1 > 99) || (sep[0] == '-')) { /* Y-M-D (iso like) */
+               tm.tm_year = p1;
+               tm.tm_mon = p2;
+               tm.tm_mday = p3;
+       }
+       else if (sep[0] == '.') {       /* Germany any other countries */
+               tm.tm_mday = p1;        /* have fixed D.M.Y format */
+               tm.tm_mon = p2;
+               tm.tm_year = p3;
+       }
+       else {
+               tm.tm_mday = p2;
+               tm.tm_mon = p1;
+               tm.tm_year = p3;
+       }
+       if ((p1 < 100) && (p2 < 100) && (p3 < 100)) {
+               if (tm.tm_year < 70) tm.tm_year += 2000;
+               else tm.tm_year += 1900;
+       }
+       /* some low-level checks */
+       if ((tm.tm_mon > 12) || (tm.tm_mon < 1) || (tm.tm_mday > 31) || (tm.tm_mday < 1))
+               fatal(MYNAME ": Could not parse date string (%s).\n", str);
+       
+       tm.tm_year -= 1900;
+       tm.tm_mon -= 1;
+       
+       return mkgmtime(&tm);
+}
+
+static int
+unicsv_parse_time(const char *str, int *msec)
+{
+       int hour, min, ct;
+       double sec;
+       char sep[2];
+       
+       ct = sscanf(str, "%d%1[.://]%d%1[.://]%lf", &hour, sep, &min, sep, &sec);
+       is_fatal(ct != 5, MYNAME ": Could not parse time string (%s).\n", str);
+       *msec = (sec - (int)sec) * 1000000;
+       return ((hour * SECONDS_PER_HOUR) + (min * 60) + (int)sec);
+}
+
 static char
 unicsv_compare_fields(char *s, const field_t *f)
 {
@@ -154,27 +263,30 @@ unicsv_compare_fields(char *s, const field_t *f)
                result = (strstr(test, name) != NULL);
        }
        else {
-               int len = strlen(name);
-               
                if (f->options & STR_LEFT) {
-                       result = (strncmp(test, name, len) == 0);
+                       result = (strncmp(test, name, strlen(name)) == 0);
                }
-/*
                else if (f->options & STR_RIGHT) {
-                       result = (strrncmp(test, name, len) == 0);
+                       result = (unicsv_strrcmp(test, name) == 0);
                }
-*/
                else {
-                       result = 0;     /* what should we do here ? */
+                       result = 0;     /* fallback to "FALSE" */
                }
        }
 
        if ((! result) && (strchr(test, ' ') != NULL)) {
+               /* replace  ' ' with '_' and try again */
                char *tmp = gstrsub(test, " ", "_");
                result = unicsv_compare_fields(tmp, f);
                xfree(tmp);
        }
-       
+       if ((! result) && (strchr(test, '-') != NULL)) {
+               /* replace  '-' with '_' and try again */
+               char *tmp = gstrsub(test, "-", "_");
+               result = unicsv_compare_fields(tmp, f);
+               xfree(tmp);
+       }
+
        if (name != f->name) {
                xfree(name);
                xfree(test);
@@ -188,7 +300,9 @@ static void
 unicsv_fondle_header(char *ibuf)
 {
        char *s;
+       char *buf = NULL;
        int i, column;
+       const cet_cs_vec_t *ascii = &cet_cs_vec_ansi_x3_4_1968; /* us-ascii */
 
        /* Convert the entire header to lower case for convenience. 
         * If we see a tab in that header, we decree it to be tabsep.
@@ -201,13 +315,26 @@ unicsv_fondle_header(char *ibuf)
                else if (*s == ';') {
                        unicsv_fieldsep = ";";
                }
+               else if (*s == '|') {
+                       unicsv_fieldsep = "|";
+               }
                else {
-                       *s = tolower(*s);
+                       continue;
                }
+               break;
+       }
+       for (s = ibuf; *s; s++) {
+               *s = tolower(*s);
+       }
+       
+       /* convert the header line into native ascii */
+       if (global_opts.charset != ascii) {
+               buf = cet_str_any_to_any(ibuf, global_opts.charset, ascii);
+               ibuf = buf;
        }
 
        column = -1;
-       while ((s = csv_lineparse(ibuf, unicsv_fieldsep, "", 0))) {
+       while ((s = csv_lineparse(ibuf, unicsv_fieldsep, "\"", 0))) {
                
                field_t *f = &fields_def[0];
                
@@ -230,6 +357,8 @@ unicsv_fondle_header(char *ibuf)
                        }
                        f++;
                }
+               if ((! f->name) && global_opts.debug_level)
+                               warning(MYNAME ": Unhandled column \"%s\".\n", s);
                
                /* handle some special items */
                if (f->type == fld_altitude) {
@@ -237,8 +366,12 @@ unicsv_fondle_header(char *ibuf)
                                unicsv_altscale = FEET_TO_METERS(1);
                        }
                }
-/* todo: date, time, maybe a few others */
+               if ((f->type == fld_time) || (f->type == fld_date)) {
+                       if (UNICSV_CONTAINS("iso"))
+                               f->type = fld_iso_time;
+               }
        }
+       if (buf) xfree(buf);
 }
 
 static void
@@ -249,12 +382,15 @@ unicsv_rd_init(const char *fname)
        
        unicsv_fields_tab = NULL;
        unicsv_fields_tab_ct = 0;
-       unicsv_data_type = wptdata;
-       unicsv_track = NULL;
+       unicsv_data_type = global_opts.objective;
+       unicsv_detect = (! (global_opts.masked_objective & (WPTDATAMASK | TRKDATAMASK | RTEDATAMASK | POSNDATAMASK)));
 
-       file_in = gbfopen(fname, "rb", MYNAME);
+       unicsv_track = unicsv_route = NULL;
+       unicsv_datum_idx = gt_lookup_datum_index(opt_datum, MYNAME);
 
-       if ((c = gbfgetstr(file_in)))
+       fin = gbfopen(fname, "rb", MYNAME);
+
+       if ((c = gbfgetstr(fin)))
                unicsv_fondle_header(c);
        else
                unicsv_fieldsep = NULL;
@@ -263,7 +399,7 @@ unicsv_rd_init(const char *fname)
 static void
 unicsv_rd_deinit(void)
 {
-       gbfclose(file_in);
+       gbfclose(fin);
        if (unicsv_fields_tab) xfree(unicsv_fields_tab);
 }
 
@@ -273,27 +409,45 @@ unicsv_parse_one_line(char *ibuf)
        char *s;
        waypoint *wpt = NULL;
        int column;
-       int  utmz = -9999;
-       double utme = 0;
-       double utmn = 0;
-       char utmc = 'N';
+       int  utm_zone = -9999;
+       double utm_easting = 0;
+       double utm_northing = 0;
+       char utm_zc = 'N';
        char bng_zone[3] = "";
        double bng_easting = 0;
        double bng_northing = 0;
-       struct tm tm;
-       char *ok;
        int checked = 0;
+       int date = -1, time = -1, msec = -1;
+       char is_localtime = 0;
 
        wpt = waypt_new();
+       wpt->latitude = -9999;
+       wpt->longitude = -9999;
+
        column = -1;
        while ((s = csv_lineparse(ibuf, unicsv_fieldsep, "\"", 0))) {
        
-               if (column > unicsv_fields_tab_ct) break;       /* extra fields on line */
+               if (column > unicsv_fields_tab_ct) break;       /* ignore extra fields on line */
 
                ibuf = NULL;
+
                column++;
                checked++;
+
                s = lrtrim(s);
+               if (! *s) continue;     /* skip empty columns */
+               
+               switch(unicsv_fields_tab[column]) {
+
+               case fld_time:
+               case fld_date:
+                       /* switch column type if it looks like an iso time string */
+                       if (strchr(s, 'T'))
+                               unicsv_fields_tab[column] = fld_iso_time;
+                       break;
+               default: ;
+               }
+
 
                switch(unicsv_fields_tab[column]) {
                
@@ -306,19 +460,19 @@ unicsv_parse_one_line(char *ibuf)
                        break;
                        
                case fld_shortname:
-                       if (*s) wpt->shortname = xstrdup(s);
+                       wpt->shortname = xstrdup(s);
                        break;
                        
                case fld_description:
-                       if (*s) wpt->description = xstrdup(s);
+                       wpt->description = xstrdup(s);
                        break;
 
                case fld_notes:
-                       if (*s) wpt->notes = xstrdup(s);
+                       wpt->notes = xstrdup(s);
                        break;
 
                case fld_url:
-                       if (*s) wpt->url = xstrdup(s);
+                       wpt->url = xstrdup(s);
                        break;
 
                case fld_altitude:
@@ -326,19 +480,19 @@ unicsv_parse_one_line(char *ibuf)
                        break;
 
                case fld_utm_zone:
-                       utmz = atoi(s);
+                       utm_zone = atoi(s);
                        break;
                        
                case fld_utm_easting:
-                       utme = atof(s);
+                       utm_easting = atof(s);
                        break;
                        
                case fld_utm_northing:
-                       utmn = atof(s);
+                       utm_northing = atof(s);
                        break;
                        
-               case fld_utm_char:
-                       utmc = toupper(s[0]);
+               case fld_utm_zone_char:
+                       utm_zc = toupper(s[0]);
                        break;
                        
                case fld_utm:
@@ -366,26 +520,26 @@ unicsv_parse_one_line(char *ibuf)
                        
                case fld_hdop:
                        wpt->hdop = atof(s);
-                       unicsv_data_type = trkdata;
+                       if (unicsv_detect) unicsv_data_type = trkdata;
                        break;
 
                case fld_pdop:
                        wpt->pdop = atof(s);
-                       unicsv_data_type = trkdata;
+                       if (unicsv_detect) unicsv_data_type = trkdata;
                        break;
 
                case fld_vdop:
                        wpt->vdop = atof(s);
-                       unicsv_data_type = trkdata;
+                       if (unicsv_detect) unicsv_data_type = trkdata;
                        break;
 
                case fld_sat:
                        wpt->sat = atoi(s);
-                       unicsv_data_type = trkdata;
+                       if (unicsv_detect) unicsv_data_type = trkdata;
                        break;
 
                case fld_fix:
-                       unicsv_data_type = trkdata;
+                       if (unicsv_detect) unicsv_data_type = trkdata;
                        if (case_ignore_strcmp(s, "none") == 0)
                                wpt->fix = fix_none;
                        else if (case_ignore_strcmp(s, "2d") == 0)
@@ -400,37 +554,27 @@ unicsv_parse_one_line(char *ibuf)
                        break;
 
                case fld_utc_date:
-                       memset(&tm, 0, sizeof(tm));
-                       if (strchr(s, '.'))
-                               ok = strptime(s, "%d.%m.%Y", &tm);
-                       else
-                               ok = strptime(s, "%d/%m/%Y", &tm);
-                       if (ok) {
-                               tm.tm_isdst = -1;
-                               wpt->creation_time += mkgmtime(&tm);
+                       if ((is_localtime < 2) && (date < 0)) {
+                               date = unicsv_parse_date(s);
+                               is_localtime = 0;
                        }
-                       else
-                               fatal(MYNAME ": Could not convert date string (%s).\n", s);
                        break;
-                       
+
                case fld_utc_time:
-                       memset(&tm, 0, sizeof(tm));
-                       if (strptime( s, "%H:%M:%S", &tm)) {
-                               wpt->creation_time += (
-                                       (SECONDS_PER_HOUR * tm.tm_hour) +
-                                       (60L * tm.tm_min) + tm.tm_sec
-                               );
+                       if ((is_localtime < 2) && (time < 0)) {
+                               time = unicsv_parse_time(s, &msec);
+                               is_localtime = 0;
                        }
                        break;
 
                case fld_speed:
                        WAYPT_SET(wpt, speed, atof(s));
-                       unicsv_data_type = trkdata;
+                       if (unicsv_detect) unicsv_data_type = trkdata;
                        break;
 
                case fld_course:
                        WAYPT_SET(wpt, course, atof(s));
-                       unicsv_data_type = trkdata;
+                       if (unicsv_detect) unicsv_data_type = trkdata;
                        break;
                        
                case fld_temperature:
@@ -443,12 +587,12 @@ unicsv_parse_one_line(char *ibuf)
 
                case fld_heartrate:
                        wpt->heartrate = atoi(s);
-                       unicsv_data_type = trkdata;
+                       if (unicsv_detect) unicsv_data_type = trkdata;
                        break;
 
                case fld_cadence:
                        wpt->cadence = atoi(s);
-                       unicsv_data_type = trkdata;
+                       if (unicsv_detect) unicsv_data_type = trkdata;
                        break;
 
                case fld_proximity:
@@ -459,6 +603,34 @@ unicsv_parse_one_line(char *ibuf)
                        WAYPT_SET(wpt, depth, atof(s));
                        break;
                        
+               case fld_symbol:
+                       wpt->icon_descr = xstrdup(s);
+                       wpt->wpt_flags.icon_descr_is_dynamic = 1;
+                       break;
+               
+               case fld_iso_time:
+                       is_localtime = 2;       /* fix result */
+                       wpt->creation_time = xml_parse_time(s, &wpt->microseconds);
+                       break;
+
+               case fld_time:
+                       if ((is_localtime < 2) && (time < 0)) {
+                               time = unicsv_parse_time(s, &msec);
+                               is_localtime = 1;
+                       }
+                       break;
+
+               case fld_date: 
+                       if ((is_localtime < 2) && (date < 0)) {
+                               date = unicsv_parse_date(s);
+                               is_localtime = 1;
+                       }
+                       break;
+
+               case fld_datetime:
+                       /* not implemented */
+                       break;
+
                case fld_terminator: /* dummy */
                        checked--;
                        break;
@@ -470,29 +642,58 @@ unicsv_parse_one_line(char *ibuf)
                return;
        }
        
-       if (utmz != -9999) {
-               GPS_Math_UTM_EN_To_Known_Datum(&wpt->latitude, &wpt->longitude,
-                       utme, utmn, utmz, utmc, DATUM_WGS84);
+       if (is_localtime < 2) { /* not fixed */
+               if ((time >= 0) && (date >= 0)) {
+                       time_t t = date + time;
+                       
+                       if (is_localtime) {
+                               struct tm tm;
+                               tm = *gmtime(&t);
+                               wpt->creation_time = mklocaltime(&tm);
+                       }
+                       else
+                               wpt->creation_time = t;
+               }
+               else if (time >= 0)
+                       wpt->creation_time = time;
+               else if (date >= 0)
+                       wpt->creation_time = date;
+               if (msec >= 0)
+                       wpt->microseconds = msec;
        }
-
-       if (bng_zone[0]) {
-               if (! GPS_Math_UKOSMap_To_WGS84_M(
+       
+       /* utm/bng can be optional */
+       if ((wpt->latitude == -9999) && (wpt->longitude == -9999)) {
+               if (utm_zone != -9999) {
+                       GPS_Math_UTM_EN_To_Known_Datum(&wpt->latitude, &wpt->longitude,
+                               utm_easting, utm_northing, utm_zone, utm_zc, DATUM_WGS84);
+               }
+               else if (bng_zone[0]) {
+                       if (! GPS_Math_UKOSMap_To_WGS84_M(
                                bng_zone, bng_easting, bng_northing,
                                &wpt->latitude, &wpt->longitude))
                        fatal(MYNAME ": Unable to convert BNG coordinates (%s %.f %.f)!\n",
                                bng_zone, bng_easting, bng_northing);
+               }
        }
 
        switch(unicsv_data_type) {
-               case trkdata:
-                       if (! unicsv_track) {
-                               unicsv_track = route_head_alloc();
-                               track_add_head(unicsv_track);
-                       }
-                       track_add_wpt(unicsv_track, wpt);
-                       break;
-               default:
-                       waypt_add(wpt);
+       case rtedata:
+               if (! unicsv_route) {
+                       unicsv_route = route_head_alloc();
+                       route_add_head(unicsv_route);
+               }
+               route_add_wpt(unicsv_route, wpt);
+               break;
+       case trkdata:
+               if (! unicsv_track) {
+                       unicsv_track = route_head_alloc();
+                       track_add_head(unicsv_track);
+               }
+               track_add_wpt(unicsv_track, wpt);
+               break;
+       default:
+               waypt_add(wpt);
        }
 }
 
@@ -503,10 +704,381 @@ unicsv_rd(void)
 
        if (unicsv_fieldsep == NULL) return;
        
-       while ((buff = gbfgetstr(file_in))) {
+       while ((buff = gbfgetstr(fin))) {
                buff = lrtrim(buff);
-               if (*buff)
-                       unicsv_parse_one_line(buff);
+               if ((*buff == '\0') || (*buff == '#')) continue;
+               unicsv_parse_one_line(buff);
+       }
+}
+
+/* =========================================================================== */
+
+static char *
+strenquote(const char *str, const char quot_char)
+{
+       int len;
+       char *cin, *cout;
+       char *tmp;
+
+       if (str == NULL) cin = "";
+       else cin = (char *)str;
+       
+       len = strlen(cin);
+       cout = tmp = xmalloc((len * 2) + 3);
+       
+       *cout++ = quot_char;
+       while (*cin) {
+               *cout++ = *cin;
+               if (*cin++ == quot_char)
+                       *cout++ = quot_char;
+       }
+       *cout++ = quot_char;
+       *cout = '\0';
+       
+       cout = xstrdup(tmp);
+       xfree(tmp);
+       return cout;
+}
+
+static void
+unicsv_print_str(const char *str)
+{
+       if (str && *str) {
+               char *cout;
+               
+               cout = strenquote(str, UNICSV_QUOT_CHAR);
+               gbfprintf(fout, "%s%s", unicsv_fieldsep, cout);
+               xfree(cout);
+       }
+       else gbfputs(unicsv_fieldsep, fout);
+}
+
+#define BIT_OF(a) (1ULL << a)
+#define FIELD_USED(a) (unicsv_outp_flags & (1ULL << a))
+
+static void 
+unicsv_waypt_enum_cb(const waypoint *wpt)
+{
+       char *shortname = (wpt->shortname) ? wpt->shortname : "";
+       
+       if (*shortname) unicsv_outp_flags |= BIT_OF(fld_shortname);
+       if (wpt->altitude != unknown_alt) unicsv_outp_flags |= BIT_OF(fld_altitude);
+       if (wpt->icon_descr && *wpt->icon_descr) unicsv_outp_flags |= BIT_OF(fld_symbol);
+       if (wpt->description && *wpt->description && (strcmp(shortname, wpt->description) != 0))
+               unicsv_outp_flags |= BIT_OF(fld_description);
+       if (wpt->notes && *wpt->notes && (strcmp(shortname, wpt->notes) != 0)) {
+               if ((! wpt->description) || (strcmp(wpt->description, wpt->notes) != 0))
+                       unicsv_outp_flags |= BIT_OF(fld_notes);
+       }
+       if (wpt->url && *wpt->url) unicsv_outp_flags |= BIT_OF(fld_url);
+       if (wpt->creation_time != 0) {
+               unicsv_outp_flags |= BIT_OF(fld_time);
+               if (wpt->creation_time >= SECONDS_PER_DAY)
+                       unicsv_outp_flags |= BIT_OF(fld_date);
+       }
+
+       if (wpt->vdop > 0) unicsv_outp_flags |= BIT_OF(fld_vdop);
+       if (wpt->hdop > 0) unicsv_outp_flags |= BIT_OF(fld_hdop);
+       if (wpt->pdop > 0) unicsv_outp_flags |= BIT_OF(fld_pdop);
+       if (wpt->sat > 0) unicsv_outp_flags |= BIT_OF(fld_sat);
+       if (wpt->heartrate != 0) unicsv_outp_flags |= BIT_OF(fld_heartrate);
+       if (wpt->cadence != 0) unicsv_outp_flags |= BIT_OF(fld_cadence);
+
+       /* "flagged" waypoint members */
+       if WAYPT_HAS(wpt, course) unicsv_outp_flags |= BIT_OF(fld_course);
+       if WAYPT_HAS(wpt, depth) unicsv_outp_flags |= BIT_OF(fld_depth);
+       if WAYPT_HAS(wpt, speed) unicsv_outp_flags |= BIT_OF(fld_speed);
+       if WAYPT_HAS(wpt, proximity) unicsv_outp_flags |= BIT_OF(fld_proximity);
+       if WAYPT_HAS(wpt, temperature) unicsv_outp_flags |= BIT_OF(fld_temperature);
+}
+
+static void 
+unicsv_waypt_disp_cb(const waypoint *wpt)
+{
+       double lat, lon, alt;
+       char *cout;
+       char *shortname = (wpt->shortname) ? wpt->shortname : "";
+       
+       unicsv_waypt_ct++;
+       
+       if (unicsv_datum_idx == DATUM_WGS84) {
+               lat = wpt->latitude;
+               lon = wpt->longitude;
+               alt = wpt->altitude;
+       }
+       else {
+               GPS_Math_WGS84_To_Known_Datum_M(wpt->latitude, wpt->longitude, 0.0,
+                       &lat, &lon, &alt, unicsv_datum_idx);
+       }
+
+       gbfprintf(fout, "%d%s", unicsv_waypt_ct, unicsv_fieldsep);
+       
+       switch(unicsv_grid_idx) {
+
+       case grid_lat_lon_ddd:
+               cout = pretty_deg_format(lat, lon, 'd', unicsv_fieldsep, 0);
+               gbfputs(cout, fout);
+               xfree(cout);
+               break;
+
+       case grid_lat_lon_dmm:
+               cout = pretty_deg_format(lat, lon, 'm', unicsv_fieldsep, 0);
+               gbfputs(cout, fout);
+               xfree(cout);
+               break;
+               
+       case grid_lat_lon_dms:
+               cout = pretty_deg_format(lat, lon, 's', unicsv_fieldsep, 0);
+               gbfputs(cout, fout);
+               xfree(cout);
+               break;
+
+       case grid_bng: {
+               char map[3];
+               double north, east;
+
+               if (! GPS_Math_WGS84_To_UKOSMap_M(wpt->latitude, wpt->longitude, &east, &north, map))
+                       fatal(MYNAME ": Some (or all?) of the coordinates cannot be displayed using \"BNG\".\n");
+               gbfprintf(fout, "%s%s%5.0f%s%5.0f",
+                       map, unicsv_fieldsep, 
+                       east, unicsv_fieldsep, 
+                       north);
+               break;
+       }
+       case grid_utm: {
+               int zone;
+               char zonec;
+               double north, east;
+
+               if (! GPS_Math_Known_Datum_To_UTM_EN(lat, lon,
+                       &east, &north, &zone, &zonec, unicsv_datum_idx))
+                       fatal(MYNAME ": Some (or all?) of the coordinates cannot be displayed using \"UTM\".\n");
+               gbfprintf(fout, "%02d%s%c%s%.0f%s%.0f",
+                       zone, unicsv_fieldsep, 
+                       zonec, unicsv_fieldsep,
+                       east, unicsv_fieldsep, 
+                       north);
+               break;
+       }
+       default:
+               gbfprintf(fout, "%.6f%s%.6f", lat, unicsv_fieldsep, lon);
+               break;
+       }
+
+       if FIELD_USED(fld_shortname) unicsv_print_str(shortname);
+       if FIELD_USED(fld_altitude) {
+               if (wpt->altitude != unknown_alt)
+                       gbfprintf(fout, "%s%.1f", unicsv_fieldsep, wpt->altitude);
+               else
+                       gbfputs(unicsv_fieldsep, fout);
+       }
+       if FIELD_USED(fld_description) unicsv_print_str(wpt->description);
+       if FIELD_USED(fld_notes) unicsv_print_str(wpt->notes);
+       if FIELD_USED(fld_symbol)
+               unicsv_print_str((wpt->icon_descr != NULL) ? wpt->icon_descr : "Waypoint");
+       if FIELD_USED(fld_depth) {
+               if WAYPT_HAS(wpt, depth)
+                       gbfprintf(fout, "%s%.1f", unicsv_fieldsep, wpt->depth);
+               else
+                       gbfputs(unicsv_fieldsep, fout);
+       }
+       if FIELD_USED(fld_proximity) {
+               if WAYPT_HAS(wpt, proximity)
+                       gbfprintf(fout, "%s%.f", unicsv_fieldsep, wpt->proximity);
+               else
+                       gbfputs(unicsv_fieldsep, fout);
+       }
+       if FIELD_USED(fld_temperature) {
+               if WAYPT_HAS(wpt, temperature)
+                       gbfprintf(fout, "%s%.1f", unicsv_fieldsep, wpt->temperature);
+               else
+                       gbfputs(unicsv_fieldsep, fout);
+       }
+       if FIELD_USED(fld_speed) {
+               if WAYPT_HAS(wpt, speed)
+                       gbfprintf(fout, "%s%.1f", unicsv_fieldsep, wpt->speed);
+               else
+                       gbfputs(unicsv_fieldsep, fout);
+       }
+       if FIELD_USED(fld_course) {
+               if WAYPT_HAS(wpt, course)
+                       gbfprintf(fout, "%s%.1f", unicsv_fieldsep, wpt->course);
+               else
+                       gbfputs(unicsv_fieldsep, fout);
+       }
+       if FIELD_USED(fld_hdop) {
+               if (wpt->hdop > 0)
+                       gbfprintf(fout, "%s%.1f", unicsv_fieldsep, wpt->hdop);
+               else
+                       gbfputs(unicsv_fieldsep, fout);
+       }
+       if FIELD_USED(fld_vdop) {
+               if (wpt->vdop > 0)
+                       gbfprintf(fout, "%s%.1f", unicsv_fieldsep, wpt->vdop);
+               else
+                       gbfputs(unicsv_fieldsep, fout);
+       }
+       if FIELD_USED(fld_pdop) {
+               if (wpt->pdop > 0)
+                       gbfprintf(fout, "%s%.1f", unicsv_fieldsep, wpt->pdop);
+               else
+                       gbfputs(unicsv_fieldsep, fout);
+       }
+       if FIELD_USED(fld_sat) {
+               if (wpt->sat > 0)
+                       gbfprintf(fout, "%s%d", unicsv_fieldsep, wpt->sat);
+               else
+                       gbfputs(unicsv_fieldsep, fout);
+       }
+       if FIELD_USED(fld_heartrate) {
+               if (wpt->heartrate != 0)
+                       gbfprintf(fout, "%s%u", unicsv_fieldsep, wpt->heartrate);
+               else
+                       gbfputs(unicsv_fieldsep, fout);
+       }
+       if FIELD_USED(fld_cadence) {
+               if (wpt->cadence != 0)
+                       gbfprintf(fout, "%s%u", unicsv_fieldsep, wpt->cadence);
+               else
+                       gbfputs(unicsv_fieldsep, fout);
+       }
+       if FIELD_USED(fld_date) {
+               if (wpt->creation_time >= SECONDS_PER_DAY) {
+                       struct tm tm;
+                       char buf[32];
+                       tm = *localtime(&wpt->creation_time);
+                       tm.tm_year += 1900;
+                       tm.tm_mon += 1;
+                       snprintf(buf, sizeof(buf), "%04d/%02d/%02d", tm.tm_year, tm.tm_mon, tm.tm_mday);
+                       gbfprintf(fout, "%s%s", unicsv_fieldsep, buf);
+               }
+               else
+                       gbfputs(unicsv_fieldsep, fout);
+       }
+       if FIELD_USED(fld_time) {
+               if (wpt->creation_time != 0) {
+                       struct tm tm;
+                       char buf[32], msec[12];
+                       tm = *localtime(&wpt->creation_time);
+                       snprintf(buf, sizeof(buf), "%02d:%02d:%02d", tm.tm_hour, tm.tm_min, tm.tm_sec);
+                       if (wpt->microseconds > 0) {
+                               snprintf(msec, sizeof(msec), ".%d", wpt->microseconds / 1000);
+                               strcat(buf, msec);
+                       }
+                       gbfprintf(fout, "%s%s", unicsv_fieldsep, buf);
+               }
+               else
+                       gbfputs(unicsv_fieldsep, fout);
+       }
+       if FIELD_USED(fld_url) unicsv_print_str(wpt->url);
+
+       gbfputs(UNICSV_LINE_SEP, fout);
+}
+
+/* --------------------------------------------------------------------------- */
+
+
+static void
+unicsv_wr_init(const char *filename)
+{
+       fout = gbfopen(filename, "w", MYNAME);
+
+       unicsv_outp_flags = 0;
+       unicsv_grid_idx = grid_unknown;
+       unicsv_datum_idx = DATUM_WGS84;
+       unicsv_fieldsep = UNICSV_FIELD_SEP;
+       unicsv_waypt_ct = 0;
+
+       if (opt_grid != NULL) {
+               int i;
+               
+               if (sscanf(opt_grid, "%d", &i)) {
+                       unicsv_grid_idx = (grid_type) i;
+                       if ((unicsv_grid_idx < GRID_INDEX_MIN) || (unicsv_grid_idx > GRID_INDEX_MAX))
+                               fatal(MYNAME ": Grid index out of range (%d..%d)!\n",
+                                       (int)GRID_INDEX_MIN, (int)GRID_INDEX_MAX);
+               }
+               else unicsv_grid_idx = gt_lookup_grid_type(opt_grid, MYNAME);
+       }
+       
+       if (unicsv_grid_idx == grid_bng)
+               /* force datum to "Ord Srvy Grt Britn" / OSGB36 */
+               /* ! ignore parameter "Datum" ! */
+               unicsv_datum_idx = DATUM_OSGB36;
+       else
+               unicsv_datum_idx = gt_lookup_datum_index(opt_datum, MYNAME);
+}
+
+static void
+unicsv_wr_deinit(void)
+{
+       gbfclose(fout);
+}
+
+static void
+unicsv_wr(void)
+{
+       switch(global_opts.objective) {
+       case wptdata:
+               waypt_disp_all(unicsv_waypt_enum_cb);
+               break;
+       case trkdata:
+               track_disp_all(NULL, NULL, unicsv_waypt_enum_cb);
+               break;
+       case rtedata:
+               route_disp_all(NULL, NULL, unicsv_waypt_enum_cb);
+               break;
+       case posndata:
+               fatal(MYNAME ": Realtime positioning not supported.\n"); 
+       }
+
+       gbfprintf(fout, "No%s", unicsv_fieldsep);
+
+       switch(unicsv_grid_idx) {
+       case grid_bng: 
+               gbfprintf(fout, "BNG-Zone%1$sBNG-East%1$sBNG-North", unicsv_fieldsep); 
+               break;
+       case grid_utm: 
+               gbfprintf(fout, "UTM-Zone%1$sUTM-Ch%1$sUTM-East%1$sUTM-North", unicsv_fieldsep);
+               break;
+       default: 
+               gbfprintf(fout, "Latitude%sLongitude", unicsv_fieldsep);
+       }
+
+       if FIELD_USED(fld_shortname) gbfprintf(fout, "%sName", unicsv_fieldsep);
+       if FIELD_USED(fld_altitude) gbfprintf(fout, "%sAltitude", unicsv_fieldsep);
+       if FIELD_USED(fld_description) gbfprintf(fout, "%sDescription", unicsv_fieldsep);
+       if FIELD_USED(fld_notes) gbfprintf(fout, "%sNotes", unicsv_fieldsep);
+       if FIELD_USED(fld_symbol) gbfprintf(fout, "%sSymbol", unicsv_fieldsep);
+       if FIELD_USED(fld_depth) gbfprintf(fout, "%sDepth", unicsv_fieldsep);
+       if FIELD_USED(fld_proximity) gbfprintf(fout, "%sProximity", unicsv_fieldsep);
+       if FIELD_USED(fld_temperature) gbfprintf(fout, "%sTemperature", unicsv_fieldsep);
+       if FIELD_USED(fld_speed) gbfprintf(fout, "%sSpeed", unicsv_fieldsep);
+       if FIELD_USED(fld_course) gbfprintf(fout, "%sCourse", unicsv_fieldsep);
+       if FIELD_USED(fld_hdop) gbfprintf(fout, "%sHDOP", unicsv_fieldsep);
+       if FIELD_USED(fld_vdop) gbfprintf(fout, "%sVDOP", unicsv_fieldsep);
+       if FIELD_USED(fld_pdop) gbfprintf(fout, "%sPDOP", unicsv_fieldsep);
+       if FIELD_USED(fld_sat) gbfprintf(fout, "%sSatellites", unicsv_fieldsep);
+       if FIELD_USED(fld_heartrate) gbfprintf(fout, "%sHeartrate", unicsv_fieldsep);
+       if FIELD_USED(fld_cadence) gbfprintf(fout, "%sCadence", unicsv_fieldsep);
+       if FIELD_USED(fld_date) gbfprintf(fout, "%sDate", unicsv_fieldsep);
+       if FIELD_USED(fld_time) gbfprintf(fout, "%sTime", unicsv_fieldsep);
+       if FIELD_USED(fld_url) gbfprintf(fout, "%sURL", unicsv_fieldsep);
+       
+       gbfputs(UNICSV_LINE_SEP, fout);
+
+       switch(global_opts.objective) {
+       case wptdata:
+               waypt_disp_all(unicsv_waypt_disp_cb);
+               break;
+       case trkdata:
+               track_disp_all(NULL, NULL, unicsv_waypt_disp_cb);
+               break;
+       case rtedata:
+               route_disp_all(NULL, NULL, unicsv_waypt_disp_cb);
+               break;
+       default:
+               break;
        }
 }
 
@@ -514,13 +1086,13 @@ unicsv_rd(void)
 
 ff_vecs_t unicsv_vecs = {
        ff_type_file,
-       { ff_cap_read, 0, 0},
+       FF_CAP_RW_ALL,
        unicsv_rd_init,
-       NULL,
+       unicsv_wr_init,
        unicsv_rd_deinit, 
-       NULL,
+       unicsv_wr_deinit,
        unicsv_rd,
-       NULL,
+       unicsv_wr,
        NULL,
        unicsv_args,
        CET_CHARSET_ASCII, 0    /* can be changed with -c ... */